package com.dlc.shop.service.impl;

import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.dlc.shop.bean.dto.BrandShopDTO;
import com.dlc.shop.bean.enums.CategoryGradeEnum;
import com.dlc.shop.bean.enums.SigningStatus;
import com.dlc.shop.bean.enums.SigningType;
import com.dlc.shop.bean.model.*;
import com.dlc.shop.common.config.Constant;
import com.dlc.shop.common.enums.StatusEnum;
import com.dlc.shop.common.exception.YamiShopBindException;
import com.dlc.shop.common.util.BeanUtil;
import com.dlc.shop.common.util.PageParam;
import com.dlc.shop.dao.SigningAuditingMapper;
import com.dlc.shop.manager.impl.LangManager;
import com.dlc.shop.service.*;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author lth
 * @Date 2021/8/19 10:18
 */
@Service
@AllArgsConstructor
public class SigningAuditingServiceImpl extends ServiceImpl<SigningAuditingMapper, SigningAuditing> implements SigningAuditingService {

    private final CategoryService categoryService;
    private final CategoryShopService categoryShopService;
    private final BrandService brandService;
    private final BrandShopService brandShopService;
    private final ProductService productService;
    private final LangManager langManager;
    private final SigningAuditingMapper signingAuditingMapper;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyCategory(Long shopId, Long userId, List<CategoryShop> categoryShopList) {
        // 校验是否已经存在申请中记录
        long applyCount = signingAuditingMapper.selectCount(Wrappers.lambdaQuery(SigningAuditing.class)
                .eq(SigningAuditing::getShopId, shopId)
                .eq(SigningAuditing::getType, SigningType.CATEGORY.value())
                .eq(SigningAuditing::getStatus, SigningStatus.PENDING_REVIEW.value())
        );
        if (applyCount != 0) {
            throw new YamiShopBindException("不能同时提交多次签约申请");
        }
        // 把之前签约不成功的分类签约信息删除
        categoryShopService.getBaseMapper().delete(Wrappers.lambdaQuery(CategoryShop.class)
                .eq(CategoryShop::getShopId, shopId)
                .ne(CategoryShop::getStatus, SigningStatus.SUCCESS.value())
        );
        // 插入分类申请签约信息
        if (CollUtil.isEmpty(categoryShopList)) {
            return;
        }
        // 获取已签约成功的分类
        List<CategoryShop> signedCategoryList = categoryShopService.list(Wrappers.lambdaQuery(CategoryShop.class)
                .eq(CategoryShop::getShopId, shopId)
                .eq(CategoryShop::getStatus, SigningStatus.SUCCESS.value())
                .orderByAsc(CategoryShop::getCategoryId)
        );
        // 校验是否已经签约成功
        categoryShopList.forEach(item -> {
            if (this.findIndexOfCategoryShopList(item.getCategoryId(), signedCategoryList) != -1) {
                throw new YamiShopBindException("存在已经签约成功的分类，请刷新页面");
            }
            item.setRate(null);
        });
        long count = categoryShopService.count(Wrappers.lambdaQuery(CategoryShop.class).eq(CategoryShop::getShopId, shopId));
        if (count + categoryShopList.size() > Constant.SIGNING_CATEGORY_LIMIT_NUM) {
            throw new YamiShopBindException("签约分类的总数不能超过200");
        }
        categoryShopService.insertBatchByShopId(categoryShopList, shopId, SigningStatus.PENDING_REVIEW.value());
        // 生成审核信息
        this.generateAuditInformation(shopId, userId, SigningType.CATEGORY.value());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void applyBrand(Long shopId, Long userId, List<BrandShopDTO> brandShopList) {
        // 校验是否已经存在申请记录
        long applyCount = signingAuditingMapper.selectCount(Wrappers.lambdaQuery(SigningAuditing.class)
                .eq(SigningAuditing::getShopId, shopId)
                .eq(SigningAuditing::getType, SigningType.BRAND.value())
                .eq(SigningAuditing::getStatus, SigningStatus.PENDING_REVIEW.value())
        );
        if (applyCount != 0) {
            throw new YamiShopBindException("不能同时提交多次签约申请");
        }
        // 把之前签约不成功的品牌签约信息删除
        brandShopService.getBaseMapper().delete(Wrappers.lambdaQuery(BrandShop.class)
                .eq(BrandShop::getShopId, shopId)
                .ne(BrandShop::getStatus, SigningStatus.SUCCESS.value())
        );
        // 插入品牌申请签约信息
        if (CollUtil.isEmpty(brandShopList)) {
            return;
        }
        List<BrandShop> signedBrandList = brandShopService.list(Wrappers.lambdaQuery(BrandShop.class)
                .eq(BrandShop::getShopId, shopId)
                .eq(BrandShop::getStatus, SigningStatus.SUCCESS.value())
                .orderByAsc(BrandShop::getBrandId)
        );
        brandShopList.forEach(item -> {
            if (this.findIndexOfBrandShopList(item.getBrandId(), signedBrandList) != -1) {
                throw new YamiShopBindException("申请的品牌中存在签约成功的品牌信息，请刷新页面");
            }
        });
        brandShopService.insertBatchByShopId(brandShopList, shopId, SigningStatus.PENDING_REVIEW.value());

        // 生成审核信息
        this.generateAuditInformation(shopId, userId, SigningType.BRAND.value());
    }

    private void generateAuditInformation(Long shopId, Long userId, Integer type) {
        SigningAuditing failSigningInfo = signingAuditingMapper.selectOne(Wrappers.lambdaQuery(SigningAuditing.class)
                .eq(SigningAuditing::getShopId, shopId)
                .eq(SigningAuditing::getType, type)
                .eq(SigningAuditing::getStatus, SigningStatus.FAIL.value())
        );
        Date now = new Date();
        SigningAuditing signingAuditing = new SigningAuditing();
        signingAuditing.setStatus(SigningStatus.PENDING_REVIEW.value());
        signingAuditing.setShopId(shopId);
        signingAuditing.setCreateTime(now);
        signingAuditing.setUpdateTime(now);
        signingAuditing.setType(type);
        signingAuditing.setUserId(userId.toString());
        if (Objects.nonNull(failSigningInfo)) {
            signingAuditing.setSigningAuditingId(failSigningInfo.getSigningAuditingId());
            signingAuditingMapper.updateById(signingAuditing);
        } else {
            signingAuditingMapper.insert(signingAuditing);
        }
    }

    @Override
    public List<Category> listApplySigningCategory(Long shopId) {
        List<Category> res = new ArrayList<>();
        List<Category> threeCategoryList = categoryService.list(Wrappers.lambdaQuery(Category.class)
                .eq(Category::getGrade, CategoryGradeEnum.THIRD.value())
                .eq(Category::getShopId, Constant.PLATFORM_SHOP_ID)
                .eq(Category::getStatus, StatusEnum.ENABLE.value())
        );
        langManager.getCategoryLang(threeCategoryList);
        List<CategoryShop> signedCategoryList = categoryShopService.list(Wrappers.lambdaQuery(CategoryShop.class)
                .eq(CategoryShop::getShopId, shopId)
                .in(CategoryShop::getStatus, SigningStatus.SUCCESS.value(), SigningStatus.PENDING_REVIEW.value())
                .orderByAsc(CategoryShop::getCategoryId)
        );
        threeCategoryList.forEach(item -> {
            if (Objects.equals(findIndexOfCategoryShopList(item.getCategoryId(), signedCategoryList), -1)) {
                res.add(item);
            }
        });
        if (CollUtil.isEmpty(res)) {
            return res;
        }
        List<Category> twoCategoryList = categoryService.listByCategoryIds(res.stream().map(Category::getParentId).collect(Collectors.toSet()));
        List<Category> oneCategoryList = categoryService.listByCategoryIds(twoCategoryList.stream().map(Category::getParentId).collect(Collectors.toSet()));
        res.addAll(twoCategoryList);
        res.addAll(oneCategoryList);
        return res;
    }

    @Override
    public List<Brand> listApplySigningBrand(Long shopId, Brand brand) {
        List<Brand> res = new ArrayList<>();
        List<Brand> brandList = brandService.listByParams(brand);
        List<BrandShop> signedBrandList = brandShopService.list(Wrappers.lambdaQuery(BrandShop.class)
                .eq(BrandShop::getShopId, shopId)
                .eq(BrandShop::getStatus, SigningStatus.SUCCESS.value())
                .orderByAsc(BrandShop::getBrandId)
        );
        brandList.forEach(item -> {
            if (Objects.equals(findIndexOfBrandShopList(item.getBrandId(), signedBrandList), -1)) {
                res.add(item);
            }
        });
        return res;
    }

    @Override
    public IPage<SigningAuditing> pageSigningAuditing(PageParam<SigningAuditing> page, SigningAuditing signingAuditing) {
        return signingAuditingMapper.pageSigningAuditing(page, signingAuditing);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void audit(SigningAuditing signingAuditing) {
        SigningAuditing dbSigningAuditing = signingAuditingMapper.selectOne(Wrappers.lambdaQuery(SigningAuditing.class)
                .eq(SigningAuditing::getShopId, signingAuditing.getShopId())
                .eq(SigningAuditing::getType, signingAuditing.getType())
                .eq(SigningAuditing::getStatus, SigningStatus.PENDING_REVIEW.value())
        );
        if (Objects.isNull(dbSigningAuditing)) {
            throw new YamiShopBindException("yami.signingAuditing.auditInformation.notFound");
        }
        Date now = new Date();
        dbSigningAuditing.setStatus(signingAuditing.getStatus());
        dbSigningAuditing.setUpdateTime(now);
        dbSigningAuditing.setAuditorId(signingAuditing.getAuditorId());
        dbSigningAuditing.setRemarks(signingAuditing.getRemarks());

        // 审核不通过
        if (Objects.equals(signingAuditing.getStatus(), SigningStatus.FAIL.value())) {
            // 把店铺处于申请中的分类或品牌签约信息置为签约失败状态
            if (Objects.equals(dbSigningAuditing.getType(), SigningType.CATEGORY.value())) {
                // 分类
                categoryShopService.changeStatusByShopIdAndStatus(dbSigningAuditing.getShopId(), SigningStatus.PENDING_REVIEW.value(), SigningStatus.FAIL.value());
            } else {
                // 品牌
                brandShopService.changeStatusByShopIdAndStatus(dbSigningAuditing.getShopId(), SigningStatus.PENDING_REVIEW.value(), SigningStatus.FAIL.value());
            }
        } else {
            // 审核成功
            if (Objects.equals(dbSigningAuditing.getType(), SigningType.CATEGORY.value())) {
                // 分类
                categoryShopService.changeStatusByShopIdAndStatus(dbSigningAuditing.getShopId(), SigningStatus.PENDING_REVIEW.value(), SigningStatus.SUCCESS.value());
            } else {
                // 品牌
                brandShopService.changeStatusByShopIdAndStatus(dbSigningAuditing.getShopId(), SigningStatus.PENDING_REVIEW.value(), SigningStatus.SUCCESS.value());
            }
        }
        signingAuditingMapper.updateById(dbSigningAuditing);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void revoke(Long shopId, Integer type) {
        SigningAuditing signingAuditing = signingAuditingMapper.selectOne(Wrappers.lambdaQuery(SigningAuditing.class)
                .eq(SigningAuditing::getShopId, shopId)
                .eq(SigningAuditing::getType, type)
                .eq(SigningAuditing::getStatus, SigningStatus.PENDING_REVIEW.value())
        );
        if (Objects.isNull(signingAuditing)) {
            throw new YamiShopBindException("yami.Auditing.applicationInfo.notFound");
        }
        // 删除申请的签约信息
        if (Objects.equals(type, SigningType.CATEGORY.value())) {
            categoryShopService.getBaseMapper().delete(Wrappers.lambdaQuery(CategoryShop.class)
                    .eq(CategoryShop::getShopId, shopId)
                    .eq(CategoryShop::getStatus, SigningStatus.PENDING_REVIEW.value())
            );
            categoryShopService.removeCacheByShopIds(Collections.singletonList(shopId));
        } else {
            brandShopService.getBaseMapper().delete(Wrappers.lambdaQuery(BrandShop.class)
                    .eq(BrandShop::getShopId, shopId)
                    .eq(BrandShop::getStatus, SigningStatus.PENDING_REVIEW.value())
            );
        }
        // 删除申请信息
        signingAuditingMapper.deleteById(signingAuditing.getSigningAuditingId());
    }

    @Override
    public void updateCategoryRate(Long categoryShopId, Double rate) {
        if (categoryShopService.count(Wrappers.lambdaQuery(CategoryShop.class).eq(CategoryShop::getCategoryShopId, categoryShopId)) != 1) {
            throw new YamiShopBindException("yami.signingAuditing.categorySigningInformation.notFound");
        }
        categoryShopService.update(Wrappers.lambdaUpdate(CategoryShop.class).set(CategoryShop::getRate, rate).eq(CategoryShop::getCategoryShopId, categoryShopId));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSigningCategory(List<CategoryShop> categoryShopList, Long shopId) {
        // 获取数据库中已保存的所有签约分类信息，并以categoryId正序排列
        List<CategoryShop> dbCategoryShopList = categoryShopService.list(Wrappers.lambdaQuery(CategoryShop.class)
                                                                   .eq(CategoryShop::getShopId, shopId));
        Map<Long, CategoryShop> shopSigningCategoryMap = dbCategoryShopList.stream().collect(Collectors.toMap(CategoryShop::getCategoryId, categoryShop -> categoryShop));
        List<CategoryShop> addCategoryList = new ArrayList<>();
        List<CategoryShop> updateCategoryList = new ArrayList<>();
        for (CategoryShop categoryShop : categoryShopList) {
            if(Objects.isNull(categoryShop.getCategoryId())){
                break;
            }
            CategoryShop dbCategoryShop = shopSigningCategoryMap.get(categoryShop.getCategoryId());
            if (Objects.isNull(dbCategoryShop)) {
                categoryShop.setRate(null);
                addCategoryList.add(categoryShop);
            }else {
                categoryShop.setCategoryShopId(dbCategoryShop.getCategoryShopId());
                categoryShop.setRate(dbCategoryShop.getRate());
                categoryShop.setStatus(StatusEnum.ENABLE.value());
                categoryShop.setShopId(shopId);
                updateCategoryList.add(categoryShop);
            }
        }

        categoryShopService.insertBatchByShopId(addCategoryList, shopId, StatusEnum.ENABLE.value());
        categoryShopService.updateBatchById(updateCategoryList);
        categoryShopService.removeCacheByShopIds(Collections.singletonList(shopId));
    }

    @Override
    public void deleteSigningCategory(Long categoryId, Long shopId) {
        categoryShopService.delete(categoryId, shopId);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addSigningBrand(List<BrandShopDTO> brandShopDTOList, Long shopId) {
        if (CollectionUtils.isEmpty(brandShopDTOList)){
            throw new YamiShopBindException("yami.shop.contracted.brand.not.null");
        }
        List<BrandShop> brandShopList = BeanUtil.mapAsList(brandShopDTOList, BrandShop.class);
        List<BrandShop> dbBrandShopList = brandShopService.list(Wrappers.lambdaQuery(BrandShop.class).eq(BrandShop::getShopId, shopId));
        Map<Long, BrandShop> shopSigningBrandMap = dbBrandShopList.stream().collect(Collectors.toMap(BrandShop::getBrandId, brandShop -> brandShop));

        List<BrandShop> addBrandList = new ArrayList<>();
        List<BrandShop> updateBrandList = new ArrayList<>();
        for (BrandShop brandShop : brandShopList) {
            BrandShop signingBrandShop = shopSigningBrandMap.get(brandShop.getBrandId());
            if (Objects.isNull(signingBrandShop)) {
                addBrandList.add(brandShop);
            } else {
                brandShop.setBrandShopId(signingBrandShop.getBrandShopId());
                brandShop.setStatus(StatusEnum.ENABLE.value());
                brandShop.setShopId(shopId);
                brandShop.setType(signingBrandShop.getType());
                updateBrandList.add(brandShop);
            }
        }
        brandShopService.insertBatchByBrandShopListAndShopId(addBrandList, shopId, StatusEnum.ENABLE.value());
        brandShopService.updateBatchById(updateBrandList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteSigningBrand(Long brandShopId) {
        BrandShop brandShop = brandShopService.getById(brandShopId);
        if (Objects.isNull(brandShop)) {
            throw new YamiShopBindException("找不到当前签约品牌,请刷新后重试");
        }
        Long brandId = brandShop.getBrandId();
        Long shopId = brandShop.getShopId();
        // 更新商品品牌
        LambdaUpdateWrapper<Product> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper.eq(Product::getBrandId, brandId).eq(Product::getShopId, shopId).set(Product::getBrandId, 0L);
        productService.update(updateWrapper);
        brandShopService.removeById(brandShopId);
    }

    @Override
    public List<SigningAuditing> listAuditingByAutidStatus() {
        List<SigningAuditing> signingAuditings = signingAuditingMapper.selectList(new LambdaQueryWrapper<SigningAuditing>().eq(SigningAuditing::getStatus, SigningStatus.PENDING_REVIEW.value()));
        return signingAuditings;
    }

    /**
     * 查找分类id在列表中的索引位置，-1代表不存在
     * @param categoryId 分类id
     * @param categoryShopList 分类id正序排列的签约分类列表
     * @return
     */
    private Integer findIndexOfCategoryShopList(Long categoryId, List<CategoryShop> categoryShopList) {
        if (Objects.isNull(categoryId) || CollUtil.isEmpty(categoryShopList)) {
            return -1;
        }
        int leftIndex = 0;
        int rightIndex = categoryShopList.size() - 1;
        int midIndex;
        while(leftIndex <= rightIndex) {
            midIndex = leftIndex + ((rightIndex - leftIndex) >>> 1);
            CategoryShop currentCategory = categoryShopList.get(midIndex);
            if (Objects.equals(categoryId, currentCategory.getCategoryId())) {
                return midIndex;
            }
            if (categoryId < currentCategory.getCategoryId()) {
                rightIndex = midIndex - 1;
            } else {
                leftIndex = midIndex + 1;
            }
        }
        return -1;
    }

    /**
     * 查找分类id在列表中的索引位置，-1代表不存在
     * @param brandId 品牌id
     * @param brandShopList 品牌id正序排列的签约品牌列表
     * @return
     */
    private Integer findIndexOfBrandShopList(Long brandId, List<BrandShop> brandShopList) {
        if (Objects.isNull(brandId) || CollUtil.isEmpty(brandShopList)) {
            return -1;
        }
        int leftIndex = 0;
        int rightIndex = brandShopList.size() - 1;
        int midIndex;
        while(leftIndex <= rightIndex) {
            midIndex = leftIndex + ((rightIndex - leftIndex) >>> 1);
            BrandShop brandShop = brandShopList.get(midIndex);
            if (Objects.equals(brandId, brandShop.getBrandId())) {
                return midIndex;
            }
            if (brandId < brandShop.getBrandId()) {
                rightIndex = midIndex - 1;
            } else {
                leftIndex = midIndex + 1;
            }
        }
        return -1;
    }
}
